// Fill out your copyright notice in the Description page of Project Settings.

#pragma once
#include "HAL/Runnable.h"
#include "HAL/RunnableThread.h"
#include "CoreMinimal.h"


DECLARE_DELEGATE(FHotChunkStateDelegate)

namespace EHotChunkThreadState
{
	enum Type
	{
		Init,

		Run,

		Complete,

		Stop,

		Join,

		Exit
	};

}
/**
 * 
 */
class HOTCHUNKTOOL_API FHotChunkThread : public FRunnable
{
public:
	using FCallback = TFunction<void()>;

	explicit FHotChunkThread(const TCHAR* InThreadName, const FCallback& InRunFunc) : ThreadName(InThreadName), CallBack(InRunFunc), ThreadState(EHotChunkThreadState::Init) {}

	virtual void Execute()
	{
		if (ThreadState == EHotChunkThreadState::Init)
		{
			WorkThread.Reset(FRunnableThread::Create(this, *ThreadName, 0));
			if (WorkThread.IsValid())
				ThreadState = EHotChunkThreadState::Run;
		}
	}

	virtual void Join()
	{
		ThreadState = EHotChunkThreadState::Join;
		StateJoinDelagete.ExecuteIfBound();
		WorkThread->WaitForCompletion();
	}

	virtual void Stop() override
	{
		ThreadState = EHotChunkThreadState::Stop;
		StateStopDelagete.ExecuteIfBound();
	}

	virtual void Exit() override
	{
		ThreadState = EHotChunkThreadState::Exit;
		StateExitDelagete.ExecuteIfBound();
	}

	virtual uint32 Run() override
	{
		ThreadState = EHotChunkThreadState::Run;
		StateRunDelagete.ExecuteIfBound();
		CallBack();
		ThreadState = EHotChunkThreadState::Complete;
		StateCompleteDelagete.ExecuteIfBound();
		return 0;
	}

	virtual EHotChunkThreadState::Type GetThreadState() const
	{
		return ThreadState;
	}

	template<class UserClass>
	FHotChunkThread& StateRun(UserClass* UserObj, typename FHotChunkStateDelegate::TRawMethodDelegate<UserClass>::FMethodPtr InMethod)
	{
		StateRunDelagete.BindRaw(UserObj, InMethod);
		return *this;
	}

	template<class UserClass>
	FHotChunkThread& StateComplete(UserClass* UserObj, typename FHotChunkStateDelegate::TRawMethodDelegate<UserClass>::FMethodPtr InMethod)
	{
		StateCompleteDelagete.BindRaw(UserObj, InMethod);
		return *this;
	}

	template<class UserClass>
	FHotChunkThread& StateStop(UserClass* UserObj, typename FHotChunkStateDelegate::TRawMethodDelegate<UserClass>::FMethodPtr InMethod)
	{
		StateStopDelagete.BindRaw(UserObj, InMethod);
		return *this;
	}

	template<class UserClass>
	FHotChunkThread& StateJoin(UserClass* UserObj, typename FHotChunkStateDelegate::TRawMethodDelegate<UserClass>::FMethodPtr InMethod)
	{
		StateJoinDelagete.BindRaw(UserObj, InMethod);
		return *this;
	}

	template<class UserClass>
	FHotChunkThread& StateExit(UserClass* UserObj, typename FHotChunkStateDelegate::TRawMethodDelegate<UserClass>::FMethodPtr InMethod)
	{
		StateExitDelagete.BindRaw(UserObj, InMethod);
		return *this;
	}

protected:

	FString ThreadName;
	volatile EHotChunkThreadState::Type ThreadState;
	TUniquePtr<FRunnableThread> WorkThread;
	FCallback CallBack;

	FHotChunkStateDelegate StateRunDelagete;
	FHotChunkStateDelegate StateCompleteDelagete;
	FHotChunkStateDelegate StateStopDelagete;
	FHotChunkStateDelegate StateJoinDelagete;
	FHotChunkStateDelegate StateExitDelagete;

private:
	FHotChunkThread(const FHotChunkThread&) = delete;
	FHotChunkThread& operator=(const FHotChunkThread&) = delete;
	
};
